/**
 * KopsoxFramework
 * Copyright (C) 2010 Free Software Foundation, Inc. <http://fsf.org/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kopsox.data.container.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.kopsox.comm.message.ErrorMessage;
import org.kopsox.comm.message.impl.ErrorMessageImpl;
import org.kopsox.comm.message.impl.ValueMessageImpl;
import org.kopsox.data.ContainerDescriptor;
import org.kopsox.data.ValidateableValue;
import org.kopsox.data.container.ValueEntity;
import org.kopsox.general.priority.PriorityEnum;
import org.kopsox.general.type.ErrorTypeEnum;
import org.kopsox.general.type.ValueType;
import org.kopsox.general.type.ValueTypeEnum;

/**
 * Creates an ValueContainer with a key.<br>
 * 
 * @author Konrad Renner - 08.09.2010
 *
 */
public class ValueEntityImpl extends ValueContainerImpl implements ValueEntity {
	
	private static final long serialVersionUID = 1348933786771308162L;
	private final Set<String> keyValueNames;

	public ValueEntityImpl(Set<String> keyValueNames){
		this(keyValueNames, new ContainerDescriptorImpl.ContainerDescriptorBuilder().build());
	}
	
	public ValueEntityImpl(Set<String> keyValueNames, ContainerDescriptor desc){
		super(desc);
		this.keyValueNames = keyValueNames;
	}
	
	@Override
	public ValueType getType() {
		return ValueTypeEnum.ENTITY;
	}

	@Override
	public Set<String> getNamesOfKeyElements() {
		return this.keyValueNames;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<ErrorMessage> setValue(final String valueName, ValidateableValue value) {
		//Null is not allowed
		if(valueName == null || value == null) {
			List<ErrorMessage> error = new ArrayList<ErrorMessage>();
			error.add(new ErrorMessageImpl(PriorityEnum.HIGHER.getPriority(), ErrorTypeEnum.NULLPARAMETER));
			return error;
		}
		
		ValueMessageImpl message;
		
		if(this.valueContainer.containsKey(valueName)) {
			
			//The key elements are forbidden to be changed!
			if(this.keyValueNames.size() == 0 || this.keyValueNames.contains(valueName)){
				ErrorMessage errmessage = new ErrorMessageImpl(PriorityEnum.HIGHEST.getPriority(), ErrorTypeEnum.KEYCHANGE);
				
				List<ErrorMessage> list = new ArrayList<ErrorMessage>(1);
				list.add(errmessage);
				
				return list;
			}
			
			ValidateableValue<Serializable> oldValue = this.valueContainer.get(valueName);
			oldValue.getValueDescriptor().setAutoValidationEnabled(super.descriptor==null?true:super.descriptor.isAutoValidationEnabled());
			
			ValidateableValue<Serializable> newValue = oldValue.changeValue(value.getValue());
			
			message = new ValueMessageImpl(newValue);
			message.setOldValue(oldValue.getValue());
			message.setTypeFromOldValue(oldValue.getType());
			message.setMessageSender(this);
			
			if(newValue.getValidationErrors().size() > 0){
				return newValue.getValidationErrors();
			}
			
			if(!super.getNamesOfDirtyValues().contains(valueName)){
				super.getNamesOfDirtyValues().add(valueName);
			}
			
			this.valueContainer.put(valueName, newValue);
		}else {
			message = new ValueMessageImpl(value);
			message.setMessageSender(this);
			
			this.valueContainer.put(valueName, value);
		}
		
		this.notifyObservers(message);
		
		return new ArrayList<ErrorMessage>();
	}
	
	

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 17;
		result = prime * result
				+ ((this.keyValueNames == null) ? 0 : this.keyValueNames.hashCode());
		result = prime * result + (super.descriptor==null?0:super.descriptor.hashCode());
		if(this.keyValueNames != null){
			for(String name:this.keyValueNames){
				result = prime * result + ((this.valueContainer.get(name) == null) ? 0 : this.valueContainer.get(name).hashCode());
			}
		}
		
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if(obj == null){
			return false;
		}
		
		if (this == obj)
			return true;
		//if (!super.equals(obj))
		//	return false;
		if (getClass() != obj.getClass())
			return false;
		ValueEntityImpl other = (ValueEntityImpl) obj;
		if (this.keyValueNames == null) {
			if (other.keyValueNames != null)
				return false;
		} else if (!this.keyValueNames.equals(other.keyValueNames))
			return false;
		
		if(this.keyValueNames != null){
			for(String name:this.keyValueNames){
				if(!super.valueContainer.get(name).equals(other.valueContainer.get(name))){
					return false;
				}
			}
		}
		
		if (getDescriptor() == null) {
            if (other.getDescriptor() != null)
                    return false;
		} else if (!getDescriptor().equals(other.getDescriptor()))
            return false;
		
		return true;
	}

	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append("ValueEntityImpl:{");
		sb.append("ContainerDescriptor=");
		sb.append(super.descriptor);
		sb.append(",keyValueNames=");
		sb.append(this.keyValueNames);
		sb.append(",valueContainer=");
		sb.append(super.valueContainer);
		sb.append(",dirtyValues:");
		sb.append(super.getNamesOfDirtyValues());
		sb.append(",validators:");
		sb.append(super.validators);
		sb.append(",observers:");
		sb.append(super.getAllObservers());
		sb.append('}');
		
		return sb.toString();
	}
}
